using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace WindowsAPI.COM
{
    /// <summary>
    /// Column Handler for all files - adds an MD5 hash of the file as a new column
    /// 
    /// Needs to be registered with RegAsm.exe and installed in the gac with GacUtil.exe
    /// </summary>
    [Guid("D389C53B-F038-47d8-9E85-32D966B37657"), ComVisible(true)]
    public class MD5ColumnHandler : ColumnProvider
    {
        const int S_OK = 0;
        const int S_FALSE = 1;


        public override int Initialize(LPCSHCOLUMNINIT psci) { return S_OK; }

        public override int GetColumnInfo(int dwIndex, out SHCOLUMNINFO psci)
        {
            psci = new SHCOLUMNINFO();

            if(dwIndex != 0)
                return S_FALSE;

            try
            {
                psci.scid.fmtid = GetType().GUID;
                psci.scid.pid = 0;

                // Cast to a ushort, because a VARTYPE is ushort and a VARENUM is int
                psci.vt = (ushort)VarEnum.VT_BSTR;
                psci.fmt = LVCFMT.LEFT;
                psci.cChars = 40;

                psci.csFlags = SHCOLSTATE.TYPE_STR;

                psci.wszTitle = "MD5 Hash";
                psci.wszDescription = "Provides an MD5 Hash of every file";
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
                return S_FALSE;
            }

            return S_OK;
        }

        public override int GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, out object pvarData)
        {
            pvarData = string.Empty;

            // Ignore directories
            if(((FileAttributes)pscd.dwFileAttributes | FileAttributes.Directory) == FileAttributes.Directory)
                return S_FALSE;

            // Only service known columns
            if(pscid.fmtid != GetType().GUID || pscid.pid != 0)
                return S_FALSE;

            try
            {
                MD5 md5 = new MD5CryptoServiceProvider();
                byte[] result;

                using(Stream stream = File.OpenRead(pscd.wszFile))
                    result = md5.ComputeHash(stream);

                var output = new StringBuilder(2 + (result.Length * 2));

                foreach(var b in result)
                    output.Append(b.ToString("x2"));
                pvarData = "0x" + output;

            }
            catch(UnauthorizedAccessException)
            {
                return S_FALSE;
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
                return S_FALSE;
            }

            return S_OK;
        }


    }
}